home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1995 February: Tool Chest / Dev.CD Feb 95 / Dev.CD Feb 95.toast / Sample Code / System 7.0 Samples / ProcDoggie 1.0a6 / UProcessGuts.inc1.p < prev    next >
Encoding:
Text File  |  1994-11-18  |  68.4 KB  |  1,929 lines  |  [TEXT/MPS ]

  1. {-------------------------------------------------------------------------------
  2. #
  3. #    Apple Macintosh Developer Technical Support
  4. #
  5. #    ProcDoggie-specific Process Manager code
  6. #
  7. #    Program:    ProcDoggie
  8. #    File:        UProcessGuts.inc1.p - Pascal Implementation
  9. #
  10. #    by:        Forrest Tanaka
  11. #
  12. #    Copyright © 1988-1991 Apple Computer, Inc.
  13. #    All rights reserved.
  14. #
  15. -------------------------------------------------------------------------------}
  16. {[j=20/57/1$] Pasmat Options}
  17. {$R-}
  18.  
  19.  
  20. (*******************************************************************************
  21. * Constants
  22. *******************************************************************************)
  23.  
  24.     CONST
  25.         rProcessListWindID = 128; {Resource ID of process list window template}
  26.         rProcessInfoWindID = 129; {Resource ID of process info window template}
  27.         rProcessInfoDitlID = 129; {Resource ID of process info dialog item list}
  28.  
  29.         kProcessListWindKind = 8;    {In windowKind field of process list windows}
  30.         kProcessInfoWindKind = 9;    {In windowKind field of process info windows}
  31.         kActivateList        = TRUE; {Pass to LActivate to specify activate list}
  32.         kScrollBarWidth      = 16;   {Width of scroll bar in pixels}
  33.  
  34.         rAppOrDAStringID = 128; {Resource ID of Application or DA string}
  35.         kAppStringInd    = 1;   {Index for Application string}
  36.         kDAStringInd     = 2;   {Index for Desk Accessory string}
  37.  
  38.         rCheckMarkID = 128; {Resource ID of checkmark string}
  39.  
  40.         kProcessNameItem   = 1;  {Dialog item # of process name}
  41.         kAppOrDAItem       = 2;  {Dialog item # of Application/DA string}
  42.         kTotalSizeItem     = 5;  {Dialog item # of Total Size readout}
  43.         kFreeSpaceItem     = 6;  {Dialog item # of Free Space readout}
  44.         kMemIndicatorItem  = 7;  {Dialog item # of partition memory indicator}
  45.         kGrayLineItem0     = 8;  {Dialog item # of first gray line}
  46.         kTypeItem          = 11; {Dialog item # of TYPE item}
  47.         kCreatorItem       = 12; {Dialog item # of Creator item}
  48.         kGrayLineItem1     = 13; {Dialog item # of second gray line}
  49.         kSusResChkItem     = 14; {Dialog item # of suspend/resume checkmark}
  50.         kWindActChkItem    = 15; {Dialog item # of window activate checkmark}
  51.         kGetClickChkItem   = 16; {Dialog item # of Get front click checkmark}
  52.         kAppDiedChkItem    = 17; {Dialog item # of App Died checkmark}
  53.         kStationeryChkItem = 18; {Dialog item # of Stationery checkmark}
  54.         kCanBackChkItem    = 19; {Dialog item # of Can Background checkmark}
  55.         kOnlyBackChkItem   = 20; {Dialog item # of Only Background checkmark}
  56.         kHighLevelChkItem  = 21; {Dialog item # of High-Level Evt checkmark}
  57.         kRHighLevelChkItem = 22; {Dialog item # of Remote High-Level checkmark}
  58.         kMultiUserChkItem  = 23; {Dialog item # of Multi-user Launch checkmark}
  59.         k32BitCleanChkItem = 24; {Dialog item # of 32-Bit Clean checkmark}
  60.  
  61.         kUsedColor = 2; {Process Info window palette color for used memory}
  62.         kFreeColor = 3; {Process Info window palette color for free memory}
  63.  
  64.  
  65. (*******************************************************************************
  66. * Types
  67. *******************************************************************************)
  68.  
  69.     TYPE
  70.         {Pointer to canonical format for number strings}
  71.         NumFormatStringPtr = ^NumFormatString;
  72.  
  73.         {Pointer to process serial number}
  74.         PSNPtr = ^ProcessSerialNumber;
  75.  
  76.  
  77. (*******************************************************************************
  78. * Variables
  79. *******************************************************************************)
  80.  
  81.     VAR
  82.         gLaunchMode: LaunchModeCode; {Open documents? Print documents?}
  83.  
  84.  
  85. {$S ProcessGuts}
  86. (*******************************************************************************
  87. * Public: SetLaunchMode
  88. *
  89. * The global variable, "gLaunchMode", is set to the launch mode specified by
  90. * "newMode".
  91. *******************************************************************************)
  92.  
  93.     PROCEDURE SetLaunchMode (newMode: LaunchModeCode);
  94.  
  95.     BEGIN
  96.         gLaunchMode := newMode
  97.     END;
  98.  
  99.  
  100. {$S ProcessGuts}
  101. (*******************************************************************************
  102. * Public: GetLaunchMode
  103. *
  104. * The value of the global variable, "gLaunchMode", is returned.
  105. *******************************************************************************)
  106.  
  107.     FUNCTION GetLaunchMode: LaunchModeCode;
  108.  
  109.     BEGIN
  110.         GetLaunchMode := gLaunchMode
  111.     END;
  112.  
  113.  
  114. {$S Main}
  115. (*******************************************************************************
  116. * Public: IsProcessListWindow
  117. *
  118. * I store a unique code in the windowKind field of every window I create so that
  119. * I can identify the kind of window it is later… like now!  I check to see if
  120. * the windowKind field of aWindow is kProcessListWindKind or not.  If it is, I
  121. * know it’s a process list window, and so IsProcessListWindow returns TRUE.
  122. *******************************************************************************)
  123.  
  124.     FUNCTION IsProcessListWindow (aWindow: WindowPtr): Boolean;
  125.  
  126.     BEGIN
  127.         IF aWindow <> NIL THEN
  128.             IsProcessListWindow := WindowPeek(aWindow)^.windowKind =
  129.                     kProcessListWindKind
  130.         ELSE
  131.             IsProcessListWindow := FALSE
  132.     END;
  133.  
  134.  
  135. {$S ProcessGuts}
  136. (*******************************************************************************
  137. * Public: CreateProcessListWindow
  138. *
  139. * I store the constant kProcessListWindKind into the windowKind field of the new
  140. * window.  When the routine IsProcessListWindow is called, it uses this field to
  141. * identify a window as a process list window.
  142. *
  143. * See the UWindowHandler unit for code to create a new window.
  144. *******************************************************************************)
  145.  
  146.     FUNCTION CreateProcessListWindow: WindowPtr;
  147.  
  148.         CONST
  149.             kDrawList      = TRUE; {Pass to LNew; list must be drawn immediately}
  150.             kHasGrow       = TRUE; {Pass to LNew; list has grow box}
  151.             kHasHorzScroll = TRUE; {Pass to LNew; list has a horizontal scroll bar}
  152.             kHasVertScroll = TRUE; {Pass to LNew; list has a vertical scroll bar}
  153.  
  154.         VAR
  155.             aWindow:        WindowPtr;  {Pointer to the process list window}
  156.             processList:    ListHandle; {Handle to the list of processes}
  157.             listRect:       Rect;       {Rectangle of list in window coords}
  158.             listDimensions: Rect;       {Dimensions of list in cells}
  159.             cellSize:       Point;      {Size of cell in pixels}
  160.             currFont:       FontInfo;   {Information about current port’s font}
  161.  
  162.         PROCEDURE HandleError (messageClass: Integer;
  163.                                messageIndex: Integer);
  164.  
  165.             VAR
  166.                 result: Integer; {Result of alert; ignored}
  167.  
  168.         BEGIN
  169.             IF aWindow <> NIL THEN
  170.                 BEGIN
  171.                     CloseWindow (aWindow);
  172.                     DisposePtr (Ptr(aWindow))
  173.                 END;
  174.             result := ShowStopAlert (messageClass, messageIndex);
  175.             gError := noErr;
  176.             CreateProcessListWindow := NIL;
  177.             EXIT (CreateProcessListWindow)
  178.         END;
  179.  
  180.     BEGIN
  181.         aWindow := NIL;
  182.  
  183.         (* Create the new window *)
  184.         aWindow := CreateWindow (rProcessListWindID);
  185.         IF gError <> noErr THEN
  186.             IF gError = memFullErr THEN
  187.                 HandleError (rMemErrMessages, kMemErrProcListOpenMsg)
  188.             ELSE IF gError = resNotFound THEN
  189.                 HandleError (rResErrMessages, kResErrAppDamageMsg)
  190.             ELSE IF gError = dsSysErr THEN
  191.                 HandleError (rMiscErrMessages, kMiscErrUnknownMsg);
  192.  
  193.         (* Set up the window *)
  194.         SetPort (aWindow);
  195.         WindowPeek(aWindow)^.windowKind := kProcessListWindKind;
  196.         TextFont (1);
  197.  
  198.         (* Create the process list *)
  199.         GetFontInfo ((*<*)currFont);
  200.         listRect := aWindow^.portRect;
  201.         listRect.right := listRect.right - kScrollBarWidth + 1;
  202.         SetRect ((*<*)listDimensions, 0, 0, 1, 0);
  203.         cellSize.h := listRect.right - listRect.left;
  204.         cellSize.v := currFont.ascent + currFont.descent + currFont.leading;
  205.         processList := LNew (listRect, listDimensions, cellSize, 128, aWindow,
  206.                 kDrawList, NOT kHasGrow, NOT kHasHorzScroll, kHasVertScroll);
  207.         IF FailLowMemory (0) THEN
  208.             HandleError (rMemErrMessages, kMemErrProcListOpenMsg);
  209.  
  210.         (* Make sure the list is activated *)
  211.         LActivate (kActivateList, processList);
  212.  
  213.         (* Save a handle to the list in the refCon of the window *)
  214.         SetWRefCon (aWindow, LongInt (processList));
  215.  
  216.         (* Set the new window as the current GrafPort and return *)
  217.         SetPort (aWindow);
  218.         CreateProcessListWindow := aWindow
  219.     END;
  220.  
  221.  
  222. {$S ProcessGuts}
  223. (*******************************************************************************
  224. * Private: EqualPSN - List Manager search proc
  225. *
  226. * The List Manager’s LSearch function can take a pointer to a routine that
  227. * checks to see if a record matches an entry in the list.  The routine must have
  228. * an interface identical to IUMagIDString.  EqualPSN is the routine that I pass
  229. * to LSearch in the IdleProcessListWindow routine.  It compares the process
  230. * serial number passed in testPSN against the process serial number contained in
  231. * the ProcessListInfoRec of a cell.  Because I already know the lengths of
  232. * ProcessListInfoRecs and ProcessSerialNumber records, I ignore the aLen and
  233. * bLen parameters.
  234. *
  235. * If the two process serial numbers refer to the same process, then EqualPSN
  236. * returns 0, otherwise it returns 1.
  237. *******************************************************************************)
  238.  
  239.     FUNCTION EqualPSN (processInfo: ProcessListInfoPtr;
  240.                        testPSN:     ProcessSerialNumberPtr;
  241.                        aLen:        Integer;
  242.                        bLen:        Integer): Integer;
  243.  
  244.         VAR
  245.             equal: Boolean; {TRUE if PSNs are equal}
  246.             error: OSErr;
  247.  
  248.     BEGIN
  249.         error := SameProcess (testPSN^, processInfo^.serialNumber, (*<*)equal);
  250.         IF equal THEN
  251.             EqualPSN := 0
  252.         ELSE
  253.             EqualPSN := 1
  254.     END;
  255.  
  256.  
  257. {$S ProcessGuts}
  258. (*******************************************************************************
  259. * Private: SetProcessListInfo - Set process list cell info
  260. *
  261. * SetProcessListInfo sets the cell specified by row (I’m only using one column,
  262. * so only the row matters) of the list specified by procList to the information
  263. * in procInfo.  My lists contain ProcessListInfoRecs, which contain only two of
  264. * the fields in ProcessInfoRecs (process name and process serial number), so I
  265. * just copy these two fields from procInfo into listInfo.  I then use LSetCell
  266. * to copy listInfo into the list.
  267. *******************************************************************************)
  268.  
  269.     PROCEDURE SetProcessListInfo (procInfo: ProcessInfoRec;
  270.                                   row:      Integer;
  271.                                   procList: ListHandle);
  272.  
  273.         VAR
  274.             listInfo: ProcessListInfoRec; {Process info from List Mgr list}
  275.             newCell:  Cell;               {Cell in which to set information}
  276.             result:   Integer;            {Result of alert; ignored}
  277.  
  278.     BEGIN
  279.         (* Copy the process name *)
  280.         BlockMove (Ptr(procInfo.processName), @listInfo.processName,
  281.                 ORD (procInfo.processName^ [0]) + 1);
  282.  
  283.         (* Copy the process serial number *)
  284.         listInfo.serialNumber := procInfo.processNumber;
  285.  
  286.         (* Set the specified cell to the new ProcessListInfoRec *)
  287.         newCell.h := 0;
  288.         newCell.v := row;
  289.         LSetCell (Ptr(@listInfo), SIZEOF (ProcessListInfoRec), newCell, procList)
  290.     END;
  291.  
  292.  
  293. {$S ProcessGuts}
  294. (*******************************************************************************
  295. * Public: IdleProcessListWindow
  296. *
  297. * I’m using a simple algorithm to keep the process list window’s process list
  298. * updated to the Process Manager’s process list, but it’ll probably be tough to
  299. * describe.  Here goes. . .
  300. *
  301. * I compare the process serial number of each entry in the Process Manager’s
  302. * list against the process serial number of the corresponding entry in
  303. * the process list window’s list.  If they match, then I just go on to the next
  304. * entries of the lists.  If they don’t match, then I search the window’s list in
  305. * case the matching process is farther down.  If I do find it farther down, then
  306. * I assume that the processes in the window’s list that come between the
  307. * matching entries in the two lists were deleted.  So, I delete those rows.  If
  308. * I don’t find it farther down, then I assume that the entry is new.  I then
  309. * insert a new row in the corresponding position of the window’s list and copy
  310. * the process information to it.
  311. *
  312. * If I run out of rows in the window’s list before getting through the entire
  313. * Process Manager list, then I just keep adding new rows to the end of the
  314. * window’s list and copying over the balance.
  315. *
  316. * If I go through the entire Process Manager list but there are left-over
  317. * entries in the window’s list, then I just delete those left-overs.
  318. *
  319. * So, that’s the algorithm.  It was the most efficient one I could come up with
  320. * that wasn’t even harder to explain.  Beware: some parts of this routine have
  321. * only gotten minimal testing, so I wouldn’t be surprised if you find bugs.
  322. *******************************************************************************)
  323.  
  324.     PROCEDURE IdleProcessListWindow (processListWindow: WindowPtr);
  325.  
  326.         VAR
  327.             procNum:        ProcessSerialNumber; {Serial number of open processes}
  328.             procInfo:       ProcessInfoRec;      {Process info from Proc Mgr list}
  329.             procName:       Str31;               {Name of the process}
  330.             listInfo:       ProcessListInfoRec;  {Process info from List Mgr list}
  331.             listInfoLength: Integer;             {Size of ProcessListInfoRec}
  332.             currCell:       Cell;                {List cell being checked}
  333.             matchCell:      Cell;                {Cell with matching PSN}
  334.             procList:       ListHandle;          {Handle to List Mgr process list}
  335.             foundMatch:     Boolean;             {Found matching List Mgr entry}
  336.             equal:          Boolean;             {Proc and List Mgr elements match}
  337.             result:         Integer;             {Result of alert; ignored}
  338.             addedProcess:   Boolean;             {TRUE if a process added to list}
  339.             error:          OSErr;
  340.  
  341.     BEGIN
  342.         (* Get the List Manager’s copy of the process list *)
  343.         procList := ListHandle(GetWRefCon (processListWindow));
  344.  
  345.         (* Start checking from start of List Mgr and Process Mgr lists *)
  346.         addedProcess := FALSE;
  347.         currCell.v := 0;
  348.         currCell.h := 0;
  349.         procNum.highLongOfPSN := 0;
  350.         procNum.lowLongOfPSN := kNoProcess;
  351.  
  352.         (* Keep looping through each open process *)
  353.         WHILE GetNextProcess ((*◊*)procNum) = noErr DO
  354.             BEGIN
  355.                 (* Get information about an open process *)
  356.                 procInfo.processInfoLength := SIZEOF (ProcessInfoRec);
  357.                 procInfo.processName := @procName;
  358.                 procInfo.processAppSpec := NIL;
  359.                 error := GetProcessInformation (procNum, (*◊*)procInfo);
  360.  
  361.                 (* Cmp List Mgr & Proc Mgr lists if enuf cells for # of processes *)
  362.                 IF PtInRect (currCell, procList^^.dataBounds) THEN
  363.                     BEGIN
  364.                         (* Get process info from List Mgr list *)
  365.                         listInfoLength := SIZEOF (ProcessListInfoRec);
  366.                         LGetCell ((*<*)@listInfo, (*◊*)listInfoLength, currCell,
  367.                                 procList);
  368.  
  369.                         (* If Proc & List Mgr lists differ, update List Mgr list *)
  370.                         error := SameProcess (procInfo.processNumber, listInfo.
  371.                                 serialNumber, (*<*)equal);
  372.                         IF NOT equal THEN
  373.                             BEGIN
  374.                                 (* See if matching process farther down List Mgr list *)
  375.                                 matchCell := currCell;
  376.                                 foundMatch := LSearch (@procInfo.processNumber,
  377.                                         SIZEOF (ProcessSerialNumber), @EqualPSN,
  378.                                         (*◊*)matchCell, procList);
  379.  
  380.                                 (* Was there a match farther down the List Mgr list? *)
  381.                                 IF foundMatch THEN
  382.                                     (* Yes, delete intervening cells *)
  383.                                     LDelRow (matchCell.v - currCell.v, currCell.v,
  384.                                             procList)
  385.                                 ELSE
  386.                                     (* No, insert the new process into List Mgr list *)
  387.                                     BEGIN
  388.                                         currCell.v := LAddRow (1, currCell.v, procList);
  389.                                         SetProcessListInfo (procInfo, currCell.v,
  390.                                                 procList)
  391.                                     END
  392.                             END
  393.                     END
  394.                 ELSE
  395.                     BEGIN
  396.                         (* Ran out of rows, add one *)
  397.                         currCell.v := LAddRow (1, currCell.v, procList);
  398.                         addedProcess := TRUE;
  399.  
  400.                         (* Set the new row to the new process information *)
  401.                         SetProcessListInfo (procInfo, currCell.v, procList)
  402.                     END;
  403.  
  404.                 (* Go to the next cell element in List Mgr list *)
  405.                 currCell.v := SUCC (currCell.v)
  406.             END;
  407.  
  408.         (* Delete any extraneous cells *)
  409.         IF currCell.v < procList^^.dataBounds.bottom THEN
  410.             LDelRow (procList^^.dataBounds.bottom - currCell.v, currCell.v,
  411.                     procList);
  412.  
  413.         (* If added processes to the list and memory low, warn *)
  414.         IF addedProcess AND FailLowMemory (0) THEN
  415.             result := ShowCautionOKAlert (rMemErrMessages, kMemErrLowMemWarnMsg)
  416.     END;
  417.  
  418.  
  419. {$S ProcessGuts}
  420. (*******************************************************************************
  421. * Public: DrawProcessListWindow
  422. *
  423. * Not much here to explain.
  424. *******************************************************************************)
  425.  
  426.     PROCEDURE DrawProcessListWindow (processListWindow: WindowPtr);
  427.  
  428.         VAR
  429.             procList: ListHandle; {Handle to List Mgr process list}
  430.  
  431.     BEGIN
  432.         SetPort (processListWindow);
  433.  
  434.         (* Get the List Manager’s copy of the process list *)
  435.         procList := ListHandle(GetWRefCon (processListWindow));
  436.  
  437.         (* Update the list *)
  438.         TextFont (1);
  439.         TextFace ([]);
  440.         TextSize (GetDefFontSize);
  441.         EraseRect (processListWindow^.portRect);
  442.         LUpdate (processListWindow^.visRgn, procList);
  443.     END;
  444.  
  445.  
  446. {$S ProcessGuts}
  447. (*******************************************************************************
  448. * Public: ClickProcessListWindow
  449. *
  450. * The List Manager is doing the lion’s share of the work.
  451. *******************************************************************************)
  452.  
  453.     PROCEDURE ClickProcessListWindow (processListWindow: WindowPtr;
  454.                                       clickEvent:        EventRecord);
  455.  
  456.         VAR
  457.             procList:    ListHandle; {Handle to List Mgr process list}
  458.             clickPos:    Point;      {Position of mouse click in window coords}
  459.             doubleClick: Boolean;    {TRUE if cell was double-clicked}
  460.  
  461.     BEGIN
  462.         SetPort (processListWindow);
  463.  
  464.         (* Get the List Manager’s copy of the process list *)
  465.         procList := ListHandle(GetWRefCon (processListWindow));
  466.  
  467.         (* Call the List Manager to handle the click *)
  468.         clickPos := clickEvent.where;
  469.         GlobalToLocal ((*◊*)clickPos);
  470.         doubleClick := LClick (clickPos, clickEvent.modifiers, procList);
  471.         IF doubleClick THEN
  472.             DoBringProcessToFront (processListWindow);
  473.     END;
  474.  
  475.  
  476. {$S ProcessGuts}
  477. (*******************************************************************************
  478. * Public: ActivateProcessListWindow
  479. *
  480. * The List Manager is called to activate/deactivate the process list window.
  481. *******************************************************************************)
  482.  
  483.     PROCEDURE ActivateProcessListWindow (processListWindow: WindowPtr;
  484.                                          becomingActive:    Boolean);
  485.  
  486.         VAR
  487.             procList: ListHandle; {Handle to List Mgr process list}
  488.  
  489.     BEGIN
  490.         SetPort (processListWindow);
  491.  
  492.         (* Get the List Manager’s copy of the process list *)
  493.         procList := ListHandle(GetWRefCon (processListWindow));
  494.  
  495.         (* Call the List Manager to activate or deactivate the list *)
  496.         LActivate (becomingActive, procList)
  497.     END;
  498.  
  499.  
  500. {$S ProcessGuts}
  501. (*******************************************************************************
  502. * Public: FixProcessListMenus
  503. *
  504. * The three launching items in the File menu are enabled as long as there’s
  505. * enough memory available.
  506. *
  507. * The List Manager routine, LGetSelect, is called to see if there are any
  508. * processes in the Process List window specified by the "processListWindow"
  509. * parameter that are selected.  If there are, then the three items in the
  510. * Process menu are enabled.  If there isn’t enough memory to safely work in,
  511. * then only the Bring Process to Front is enabled.
  512. *******************************************************************************)
  513.  
  514.     PROCEDURE FixProcessListMenus (processListWindow: WindowPtr);
  515.  
  516.         CONST
  517.             kFindNext = TRUE; {Pass to LGetSelect to find sequence of selections}
  518.  
  519.         VAR
  520.             aMenu:    MenuHandle; {Handle to any menu we’re checking on}
  521.             procList: ListHandle; {Handle to List Mgr process list}
  522.             aCell:    Cell;       {Cell of process list}
  523.  
  524.     BEGIN
  525.         (* Get the List Manager’s copy of the process list *)
  526.         procList := ListHandle(GetWRefCon (processListWindow));
  527.  
  528.         (* Enable the File menu launch items *)
  529.         aMenu := GetMenuHandle (mFile);
  530.         IF NOT FailLowMemory (0) THEN
  531.             BEGIN
  532.                 EnableItem (aMenu, iLaunchFore);
  533.                 EnableItem (aMenu, iLaunchBack);
  534.                 EnableItem (aMenu, iLaunchTo)
  535.             END;
  536.  
  537.         (* Undim the Process menu items *)
  538.         aMenu := GetMenuHandle (mProcess);
  539.         aCell.v := 0;
  540.         aCell.h := 0;
  541.         IF LGetSelect (kFindNext, (*◊*)aCell, procList) THEN
  542.             BEGIN
  543.                 (* There’s ≥ 1 sel’d process, enable Bring Process to Front *)
  544.                 EnableItem (aMenu, iBringFront);
  545.  
  546.                 (* Only enable other two items if enough memory to safely work *)
  547.                 IF NOT FailLowMemory (0) THEN
  548.                     BEGIN
  549.                         EnableItem (aMenu, iShowProcessInfo);
  550.                         EnableItem (aMenu, iTerminateProcess)
  551.                     END
  552.             END
  553.     END;
  554.  
  555.  
  556. {$S Main}
  557. (*******************************************************************************
  558. * Public: IsProcessInfoWindow
  559. *
  560. * I store a unique code in the windowKind field of every window I create so that
  561. * I can identify the kind of window it is later… like now!  I check to see if
  562. * the windowKind field of aWindow is kProcessInfoWindKind or not.  If it is, I
  563. * know it’s a process info window, and so IsProcessInfoWindow returns TRUE.
  564. *******************************************************************************)
  565.  
  566.     FUNCTION IsProcessInfoWindow (aWindow: WindowPtr): Boolean;
  567.  
  568.     BEGIN
  569.         IF aWindow <> NIL THEN
  570.             IsProcessInfoWindow := WindowPeek(aWindow)^.windowKind =
  571.                     kProcessInfoWindKind
  572.         ELSE
  573.             IsProcessInfoWindow := FALSE
  574.     END;
  575.  
  576.  
  577. {$S ProcessGuts}
  578. (*******************************************************************************
  579. * Private: GetNumberParts - Get the default number parts table
  580. *
  581. * To use the Script Manager’s number conversion routines, the number parts table
  582. * in the 'itl4' resource must be retrieved.  This routine gets the itl4 resource
  583. * and copies the number parts table into the "partsTable" parameter.
  584. *
  585. * If the retrieval was successful, then TRUE is returned.  If the itl4 resource
  586. * couldn’t be loaded for some reason, then FALSE is returned.
  587. *******************************************************************************)
  588.  
  589.     FUNCTION GetNumberParts (VAR partsTable: NumberParts): Boolean;
  590.  
  591.         VAR
  592.             intl4: Itl4Handle; {Handle to the itl4 resource}
  593.  
  594.     BEGIN
  595.         intl4 := Itl4Handle(GetIntlResource (4));
  596.         IF intl4 <> NIL THEN
  597.             BEGIN
  598.                 partsTable := NumberPartsPtr(ORD (intl4^) + intl4^^.
  599.                         defPartsOffset)^;
  600.                 GetNumberParts := TRUE
  601.             END
  602.         ELSE
  603.             GetNumberParts := FALSE
  604.     END;
  605.  
  606.  
  607. {$S ProcessGuts}
  608. (*******************************************************************************
  609. * Private: TextLineBox - Draw a line of text into a box.
  610. *
  611. * This routine is very similar to TextEdit’s TETextBox routine, and in fact it
  612. * takes the same parameters.  But TextLineBox draws a single line of text
  613. * specified by "textLine" and having the length specified by "length" into
  614. * the current GrafPort, ignoring carriage returns and word-wrap.  This means
  615. * that there’s less overhead than TETextBox.  But TETextBox itself is optimized for
  616. * single lines of text, so there is an ulterior motive for this routine.
  617. * TETextBox erases the entire box before drawing the text.  This results in a
  618. * slight flicker if TETextBox is called to draw over previous text.  TextLineBox
  619. * only erases the part of the box that isn’t covered with the text specified by
  620. * "textLine".  Also, the text is drawn in srcCopy mode.  If TextLineBox is
  621. * called to draw over existing text, the result should be a smooth transition
  622. * from one text to another, without flicker.
  623. *******************************************************************************)
  624.  
  625.     PROCEDURE TextLineBox (textLine: Ptr;
  626.                            length:   Integer;
  627.                            box:      Rect;
  628.                            just:     Integer);
  629.  
  630.         VAR
  631.             currPort:     GrafPtr;   {Pointer to the current GrafPort}
  632.             currTextMode: Integer;   {Current text mode}
  633.             currFont:     FontInfo;  {Current font information}
  634.             lineWidth:    Integer;   {Width of line of text in pixels}
  635.             spareSpace:   Integer;   {Width of box - width of text}
  636.             spareRect:    Rect;      {Rectangle of area not filled with text}
  637.             currClip:     RgnHandle; {Handle to the current clip region}
  638.  
  639.     BEGIN
  640.         (* Save the current clip region and set the clip region to "box" *)
  641.         currClip := NewRgn;
  642.         GetClip ((*<*)currClip);
  643.         ClipRect (box);
  644.  
  645.         (* Save the current text mode and set it to srcCopy *)
  646.         GetPort ((*<*)currPort);
  647.         currTextMode := currPort^.txMode;
  648.         TextMode (srcCopy);
  649.  
  650.         (* If default justification, set to real justification based on SysJust *)
  651.         IF just = teFlushDefault THEN
  652.             IF GetSysDirection = 0 THEN
  653.                 just := teFlushLeft
  654.             ELSE
  655.                 just := teFlushRight;
  656.  
  657.         (* Move pen to baseline on left side of box *)
  658.         GetFontInfo (currFont);
  659.         MoveTo (box.left, box.top + currFont.ascent);
  660.  
  661.         (* Find the width of the specified text *)
  662.         lineWidth := TextWidth (textLine, 0, length);
  663.  
  664.         (* Adjust the pen for centered or right-aligned text *)
  665.         IF just <> teFlushLeft THEN
  666.             BEGIN
  667.                 spareSpace := box.right - box.left - lineWidth;
  668.                 IF just = teCenter THEN
  669.                     spareSpace := spareSpace DIV 2;
  670.                 Move (spareSpace, 0);
  671.             END;
  672.  
  673.         (* Erase area at end(s) of text *)
  674.         spareRect := box;
  675.         IF just = teFlushLeft THEN
  676.             spareRect.left := spareRect.left + lineWidth
  677.         ELSE
  678.             BEGIN
  679.                 IF just = teCenter THEN
  680.                     BEGIN
  681.                         spareRect.left := spareRect.left + spareSpace + lineWidth;
  682.                         EraseRect (spareRect)
  683.                     END;
  684.                 spareRect.left := box.left;
  685.                 spareRect.right := spareRect.left + spareSpace;
  686.             END;
  687.         IF NOT EmptyRect (spareRect) THEN
  688.             EraseRect (spareRect);
  689.  
  690.         (* Draw the line of text *)
  691.         DrawText (textLine, 0, length);
  692.  
  693.         (* Restore the port to its normal state *)
  694.         TextMode (currTextMode);
  695.         SetClip (currClip);
  696.         DisposeRgn (currClip)
  697.     END;
  698.  
  699.  
  700. {$S ProcessGuts}
  701. (*******************************************************************************
  702. * Private: FindProcessInfoWindow - Find a process info window for a process
  703. *
  704. * This routine searches the window list for a process info window that
  705. * represents the process with the process serial number specified by
  706. * "searchPSN".  Every process info window has a handle to the process serial
  707. * number of the process it represents in the refCon field of the window.  The
  708. * Process Manager routine, SameProcess, does the work of comparing the given
  709. * process serial number against the process serial number in the refCon.
  710. *
  711. * If a window for the specified process is found, a pointer to that window is
  712. * returned.  If there isn’t any window representing the given process, then NIL
  713. * is returned.
  714. *******************************************************************************)
  715.  
  716.     FUNCTION FindProcessInfoWindow (searchPSN: ProcessSerialNumber): WindowPtr;
  717.  
  718.         VAR
  719.             testWindow: WindowPtr; {Pointer to window we’re testing}
  720.             found:      Boolean;   {TRUE if matching process info window was found}
  721.             psnHandle:  Handle;    {Handle to PSN of window’s process info window}
  722.             error:      OSErr;
  723.  
  724.     BEGIN
  725.         found := FALSE;
  726.         testWindow := FrontWindow;
  727.  
  728.         (* Loop until the window is found or every window has been searched *)
  729.         WHILE (testWindow <> NIL) AND (NOT found) DO
  730.             BEGIN
  731.                 IF IsProcessInfoWindow (testWindow) THEN
  732.                     BEGIN
  733.                         (* Get the PSN of the window from its refCon *)
  734.                         psnHandle := Handle(GetWRefCon (testWindow));
  735.  
  736.                         (* Compare window’s PSN against searchPSN *)
  737.                         HLock (psnHandle);
  738.                         error := SameProcess (searchPSN, PSNPtr(psnHandle^)^,
  739.                                 (*<*)found);
  740.                         HUnlock (psnHandle)
  741.                     END;
  742.  
  743.                 (* Go to the next window in the window list *)
  744.                 IF NOT found THEN
  745.                     testWindow := WindowPtr(WindowPeek(testWindow)^.nextWindow)
  746.             END;
  747.  
  748.         (* Return pointer to matching process info window, or NIL if no match *)
  749.         FindProcessInfoWindow := testWindow
  750.     END;
  751.  
  752.  
  753. {$S ProcessGuts}
  754. (*******************************************************************************
  755. * Private: CreateProcessInfoWindow - Create a process info window
  756. *
  757. * This routine is called to create a new process info window and to display it
  758. * on the screen.  A pointer to the window is returned.  If there wasn’t enough
  759. * memory to open the new window, or if there was some other problem preventing
  760. * the window from being completely created, then an alert indicating the problem
  761. * is presented to the user and NIL is returned.
  762. *
  763. * I store the constant kProcessInfoWindKind into the windowKind field of the new
  764. * window.  When the routine IsProcessInfoWindow is called, it uses this field to
  765. * identify a window as a process info window.
  766. *******************************************************************************)
  767.  
  768.     FUNCTION CreateProcessInfoWindow: WindowPtr;
  769.  
  770.         VAR
  771.             aWindow: WindowPtr; {Pointer to the new window}
  772.             error:   OSErr;
  773.  
  774.         PROCEDURE HandleError (messageClass: Integer;
  775.                                messageIndex: Integer);
  776.  
  777.             VAR
  778.                 result: Integer; {Result of alert; ignored}
  779.  
  780.         BEGIN
  781.             IF aWindow <> NIL THEN
  782.                 CloseProcessInfoWindow (aWindow);
  783.             result := ShowStopAlert (messageClass, messageIndex);
  784.             gError := noErr;
  785.             CreateProcessInfoWindow := NIL;
  786.             EXIT (CreateProcessInfoWindow)
  787.         END;
  788.  
  789.     BEGIN
  790.         aWindow := NIL;
  791.  
  792.         (* Create the new window *)
  793.         aWindow := CreateDialog (rProcessInfoWindID);
  794.         IF aWindow = NIL THEN
  795.             IF gError = memFullErr THEN
  796.                 HandleError (rMemErrMessages, kMemErrProcInfoOpenMsg)
  797.             ELSE IF gError = resNotFound THEN
  798.                 HandleError (rResErrMessages, kResErrAppDamageMsg)
  799.             ELSE IF gError = dsSysErr THEN
  800.                 HandleError (rMiscErrMessages, kMiscErrUnknownMsg);
  801.  
  802.         (* Set up the window *)
  803.         SetPort (aWindow);
  804.         WindowPeek(aWindow)^.windowKind := kProcessInfoWindKind;
  805.  
  806.         (* Install the dialog items *)
  807.         error := InstallDialogItems (aWindow, rProcessInfoDitlID);
  808.         IF error <> noErr THEN
  809.             IF error = memFullErr THEN
  810.                 HandleError (rMemErrMessages, kMemErrProcInfoOpenMsg)
  811.             ELSE IF error = resNotFound THEN
  812.                 HandleError (rResErrMessages, kResErrAppDamageMsg)
  813.             ELSE IF error = dsSysErr THEN
  814.                 HandleError (rMiscErrMessages, kMiscErrUnknownMsg);
  815.  
  816.         CreateProcessInfoWindow := aWindow
  817.     END;
  818.  
  819.  
  820. {$S ProcessGuts}
  821. (*******************************************************************************
  822. * Private: DrawGrayLine - Draw a gray line into a dialog item
  823. *
  824. * DrawGrayLine draws a line from the top-left corner of "grayLineRect" to its
  825. * bottom-right corner.  On a non-Color QuickDraw Macintosh, this line is simply
  826. * drawn using the 50% gray pattern.  On a Color QuickDraw Macintosh, a gray
  827. * type-2 pattern is created with a gray color.  When this pattern is used to
  828. * draw to the screen, it is drawn using the specified color if possible.  If
  829. * there aren’t enough available colors, the color is dithered using the closest
  830. * available colors.
  831. *******************************************************************************)
  832.  
  833.     PROCEDURE DrawGrayLine (grayLineRect: Rect);
  834.  
  835.         VAR
  836.             qdVersion:   LongInt;      {QuickDraw version number}
  837.             grayColor:   RGBColor;     {Color of gray line}
  838.             grayPattern: PixPatHandle; {Handle to the gray pattern}
  839.             result:      OSErr;
  840.  
  841.     BEGIN
  842.         grayPattern := NIL;
  843.         PenNormal;
  844.  
  845.         (* See if Color QuickDraw is on this machine or not *)
  846.         result := Gestalt (gestaltQuickdrawVersion, (*<*)qdVersion);
  847.         IF qdVersion = gestaltOriginalQD THEN
  848.             (* Nope, just draw a 50% gray pattern *)
  849.             PenPat (qd.gray)
  850.         ELSE
  851.             (* Yup, make a true gray pattern that can be dithered to the screen *)
  852.             BEGIN
  853.                 grayColor.red := $7FFF;
  854.                 grayColor.green := $7FFF;
  855.                 grayColor.blue := $7FFF;
  856.                 grayPattern := NewPixPat;
  857.                 MakeRGBPat (grayPattern, grayColor);
  858.                 PenPixPat (grayPattern);
  859.             END;
  860.  
  861.         (* Draw the line *)
  862.         MoveTo (grayLineRect.left, grayLineRect.top);
  863.         LineTo (grayLineRect.right, grayLineRect.bottom);
  864.  
  865.         (* Clean up *)
  866.         IF grayPattern <> NIL THEN
  867.             DisposePixPat (grayPattern);
  868.         PenNormal
  869.     END;
  870.  
  871.  
  872. {$S ProcessGuts}
  873. (*******************************************************************************
  874. * Private: SetUpProcessInfoItems - Set up process information static text items
  875. *
  876. * This routine sets up the text of the static text items in the process info
  877. * window specified by "processInfoWindow" to reflect the process information
  878. * passed in the "processInfo" parameter.  Only the process information that
  879. * doesn’t change while a process is active is set in this routine.  Information
  880. * that changes while a process is active is set and drawn in the
  881. * IdleProcessInfoWindow routine.
  882. *
  883. * Numbers are converted to strings using the ExtendedToString routine.  ExtendedToString
  884. * requires a script-independent canonical number format so that the resulting
  885. * string appears with the proper thousands separator regardless of the script
  886. * in use.  I previously created a canonical number format that has the form
  887. * ###,###,### in the U.S and saved it in a resource of type NUMF.
  888. *******************************************************************************)
  889.  
  890.     PROCEDURE SetUpProcessInfoItems (processInfoWindow: WindowPtr;
  891.                                      processInfo:       ProcessInfoRec);
  892.  
  893.         VAR
  894.             itemString:    Str255;       {"Application" or "Desk Accessory" string}
  895.             blankString:   Integer;      {Dummy empty string}
  896.             checkString:   StringPtr;    {Ptr either to check mark or blankString}
  897.             checkStrHnd:   StringHandle; {Handle to check mark string}
  898.             partitionSize: extended;     {Size of processes partition}
  899.             partitionSize80: extended80; {Size of processes partition}
  900.             partsTable:    NumberParts;  {Number parts table from itl4 resource}
  901.             canonRsrc:     Handle;       {Hnd to canonical # format '###,###,###'}
  902.             status:        FormatStatus; {Status of #->String conversion}
  903.             success:       Boolean;      {TRUE if GetNumberParts call worked}
  904.  
  905.     BEGIN
  906.         (* Set process name *)
  907.         SetStatTextItem (processInfoWindow, kProcessNameItem, @processInfo.
  908.                 processName^ [1], ORD (processInfo.processName^ [0]));
  909.  
  910.         (* Set Application or Desk Accessory string *)
  911.         IF BAND (processInfo.processMode, modeDeskAccessory) <> 0 THEN
  912.             GetIndString ((*◊*)itemString, rAppOrDAStringID, kDAStringInd)
  913.         ELSE
  914.             GetIndString ((*◊*)itemString, rAppOrDAStringID, kAppStringInd);
  915.         SetStatTextItem (processInfoWindow, kAppOrDAItem, @itemString [1],
  916.                 ORD (itemString [0]));
  917.  
  918.         (* Set partition size item *)
  919.         partitionSize := processInfo.processSize DIV 1024;
  920.         success := GetNumberParts (partsTable);
  921.         IF success THEN
  922.             BEGIN
  923.                 (* Get the canonical number format I created earlier *)
  924.                 canonRsrc := Get1Resource ('NUMF', 0);
  925.                 IF canonRsrc <> NIL THEN
  926.                     BEGIN
  927.                         (* Convert partition size from extended to formatted string *)
  928.                         HLock (canonRsrc);
  929.                         BlockMove (Ptr(@partitionSize), Ptr(@partitionSize80), SIZEOF (extended80));
  930.                         status := ExtendedToString (partitionSize80,
  931.                                 NumFormatStringPtr(canonRsrc^)^, partsTable,
  932.                                 (*<*)itemString);
  933.                         HUnlock (canonRsrc);
  934.  
  935.                         (* Set Total Size item to formatted partition size string *)
  936.                         SetStatTextItem (processInfoWindow, kTotalSizeItem,
  937.                                 @itemString [1], ORD (itemString [0]))
  938.                     END
  939.             END;
  940.  
  941.         (* Set type and creator *)
  942.         SetStatTextItem (processInfoWindow, kTypeItem, @processInfo.processType,
  943.                 SIZEOF (LongInt));
  944.         SetStatTextItem (processInfoWindow, kCreatorItem, @processInfo.
  945.                 processSignature, SIZEOF (OSType));
  946.  
  947.         (* Initialize the checkmark and blank strings *)
  948.         checkStrHnd := GetString (rCheckMarkID);
  949.         IF checkStrHnd <> NIL THEN
  950.             BlockMove (Ptr(checkStrHnd^), @itemString, ORD (checkStrHnd^^ [0]) + 1)
  951.         ELSE
  952.             itemString [0] := CHR (0);
  953.         blankString := 0;
  954.  
  955.         (* Check the suspend/resume flag *)
  956.         IF BAND (processInfo.processMode, modeNeedSuspendResume) <> 0 THEN
  957.             checkString := @itemString
  958.         ELSE
  959.             checkString := @blankString;
  960.         SetStatTextItem (processInfoWindow, kSusResChkItem, @checkString^ [1],
  961.                 ORD (checkString^ [0]));
  962.  
  963.         (* Check the window activate flag *)
  964.         IF BAND (processInfo.processMode, modeDoesActivateOnFGSwitch) <> 0 THEN
  965.             checkString := @itemString
  966.         ELSE
  967.             checkString := @blankString;
  968.         SetStatTextItem (processInfoWindow, kWindActChkItem, @checkString^ [1],
  969.                 ORD (checkString^ [0]));
  970.  
  971.         (* Check the window activate flag *)
  972.         IF BAND (processInfo.processMode, modeGetFrontClicks) <> 0 THEN
  973.             checkString := @itemString
  974.         ELSE
  975.             checkString := @blankString;
  976.         SetStatTextItem (processInfoWindow, kGetClickChkItem, @checkString^ [1],
  977.                 ORD (checkString^ [0]));
  978.  
  979.         (* Check the window activate flag *)
  980.         IF BAND (processInfo.processMode, modeGetAppDiedMsg) <> 0 THEN
  981.             checkString := @itemString
  982.         ELSE
  983.             checkString := @blankString;
  984.         SetStatTextItem (processInfoWindow, kAppDiedChkItem, @checkString^ [1],
  985.                 ORD (checkString^ [0]));
  986.  
  987.         (* Check the window activate flag *)
  988.         IF BAND (processInfo.processMode, modeStationeryAware) <> 0 THEN
  989.             checkString := @itemString
  990.         ELSE
  991.             checkString := @blankString;
  992.         SetStatTextItem (processInfoWindow, kStationeryChkItem, @checkString^ [1],
  993.                 ORD (checkString^ [0]));
  994.  
  995.         (* Check the window activate flag *)
  996.         IF BAND (processInfo.processMode, modeCanBackground) <> 0 THEN
  997.             checkString := @itemString
  998.         ELSE
  999.             checkString := @blankString;
  1000.         SetStatTextItem (processInfoWindow, kCanBackChkItem, @checkString^ [1],
  1001.                 ORD (checkString^ [0]));
  1002.  
  1003.         (* Check the window activate flag *)
  1004.         IF BAND (processInfo.processMode, modeOnlyBackground) <> 0 THEN
  1005.             checkString := @itemString
  1006.         ELSE
  1007.             checkString := @blankString;
  1008.         SetStatTextItem (processInfoWindow, kOnlyBackChkItem, @checkString^ [1],
  1009.                 ORD (checkString^ [0]));
  1010.  
  1011.         (* Check the window activate flag *)
  1012.         IF BAND (processInfo.processMode, modeHighLevelEventAware) <> 0 THEN
  1013.             checkString := @itemString
  1014.         ELSE
  1015.             checkString := @blankString;
  1016.         SetStatTextItem (processInfoWindow, kHighLevelChkItem, @checkString^ [1],
  1017.                 ORD (checkString^ [0]));
  1018.  
  1019.         (* Check the window activate flag *)
  1020.         IF BAND (processInfo.processMode, modeLocalAndRemoteHLEvents) <> 0 THEN
  1021.             checkString := @itemString
  1022.         ELSE
  1023.             checkString := @blankString;
  1024.         SetStatTextItem (processInfoWindow, kRHighLevelChkItem, @checkString^ [1],
  1025.                 ORD (checkString^ [0]));
  1026.  
  1027.         (* Check the window activate flag *)
  1028.         IF BAND (processInfo.processMode, modeMultiLaunch) <> 0 THEN
  1029.             checkString := @itemString
  1030.         ELSE
  1031.             checkString := @blankString;
  1032.         SetStatTextItem (processInfoWindow, kMultiUserChkItem, @checkString^ [1],
  1033.                 ORD (checkString^ [0]));
  1034.  
  1035.         (* Check the window activate flag *)
  1036.         IF BAND (processInfo.processMode, mode32BitCompatible) <> 0 THEN
  1037.             checkString := @itemString
  1038.         ELSE
  1039.             checkString := @blankString;
  1040.         SetStatTextItem (processInfoWindow, k32BitCleanChkItem, @checkString^ [1],
  1041.                 ORD (checkString^ [0]));
  1042.  
  1043.     END;
  1044.  
  1045.  
  1046. {$S ProcessGuts}
  1047. (*******************************************************************************
  1048. * Public: IdleProcessInfoWindow
  1049. *
  1050. * The memory indicator and the free memory readout are updated with the current
  1051. * values.
  1052. *
  1053. * The free memory readout is a static text item in the DITL, but there’s no text
  1054. * for it.  Instead, I’m drawing into that item’s rectangle using TextLineBox.
  1055. * I set the item up as a static text item just so that I can specify the type
  1056. * characteristics of the free memory readout from the DITL resource rather than
  1057. * hard-coding them in this routine.
  1058. *
  1059. * Numbers are converted to strings using the ExtendedToString routine.  ExtendedToString
  1060. * requires a script-independent canonical number format so that the resulting
  1061. * string appears with the proper thousands separator regardless of the script
  1062. * in use.  I previously created a canonical number format that has the form
  1063. * ###,###,### in the U.S and saved it in a resource of type NUMF.
  1064. *******************************************************************************)
  1065.  
  1066.     PROCEDURE IdleProcessInfoWindow (processInfoWindow: WindowPtr);
  1067.  
  1068.         VAR
  1069.             processInfo:   ProcessInfoRec; {Process info for window’s process}
  1070.             psnHandle:     Handle;         {Handle to PSN of window’s process}
  1071.             freeSpace:     extended;       {Amount of free space in partition}
  1072.             freeSpace80:   extended80;     {Amount of free space in partition}
  1073.             canonRsrc:     Handle;         {canonical # format '###,###,###'}
  1074.             freeSpaceStr:  Str255;         {String representation of freeSpace}
  1075.             status:        FormatStatus;   {Status of #->string conversion}
  1076.             partsTable:    NumberParts;    {Number parts table from itl4 resource}
  1077.             itemType:      TypeInfoRec;    {Type information for free mem readout}
  1078.             itemRect:      Rect;           {Rectangle of dialog item}
  1079.             freeAngle:     Integer;        {Angle between free and full memory}
  1080.             aColor:        RGBColor;       {Color to draw memory indicator}
  1081.             qdVersion:     LongInt;        {Version of QuickDraw on this machine}
  1082.             success:       Boolean;        {TRUE if GetNumberParts call worked}
  1083.             error:         OSErr;
  1084.  
  1085.     BEGIN
  1086.         SetPort (processInfoWindow);
  1087.         PenNormal;
  1088.  
  1089.         (* Get the PSN of the process associated with processInfoWindow *)
  1090.         psnHandle := Handle(GetWRefCon (processInfoWindow));
  1091.  
  1092.         (* Get information about an open process *)
  1093.         processInfo.processInfoLength := SIZEOF (ProcessInfoRec);
  1094.         processInfo.processName := NIL;
  1095.         processInfo.processAppSpec := NIL;
  1096.         HLock (psnHandle);
  1097.         error := GetProcessInformation (PSNPtr(psnHandle^)^, (*◊*)processInfo);
  1098.         HUnlock (psnHandle);
  1099.  
  1100.         (* Check to see whether the process still exists *)
  1101.         IF error = procNotFound THEN
  1102.             (* Process terminated, so close this process info window *)
  1103.             CloseProcessInfoWindow (processInfoWindow)
  1104.         ELSE
  1105.             BEGIN
  1106.                 (* Starting here, convert amount of free space to a string *)
  1107.                 freeSpace := processInfo.processFreeMem DIV 1024;
  1108.  
  1109.                 (* Get number parts table from itl4 *)
  1110.                 success := GetNumberParts ((*<*)partsTable);
  1111.                 IF success THEN
  1112.                     BEGIN
  1113.                         (* Get my canonical number format *)
  1114.                         canonRsrc := Get1Resource ('NUMF', 0);
  1115.                         IF canonRsrc <> NIL THEN
  1116.                             BEGIN
  1117.                                 (* Convert free space to equivalent string *)
  1118.                                 HLock (canonRsrc);
  1119.                                 BlockMove (Ptr(@freeSpace), Ptr(@freeSpace80), SIZEOF (extended80));
  1120.                                 status := ExtendedToString (freeSpace80,
  1121.                                         NumFormatStringPtr(canonRsrc^)^, partsTable,
  1122.                                         (*<*)freeSpaceStr);
  1123.                                 HUnlock (canonRsrc);
  1124.  
  1125.                                 (* Get the item rectangle of the free-space readout *)
  1126.                                 GetDialogItemRect (processInfoWindow, kFreeSpaceItem,
  1127.                                         (*<*)itemRect);
  1128.  
  1129.                                 (* Get the font characteristics of the stat text item *)
  1130.                                 GetStatTextFontInfo (processInfoWindow, kFreeSpaceItem,
  1131.                                         (*<*)itemType);
  1132.  
  1133.                                 (* Draw the free-space readout *)
  1134.                                 TextFont (itemType.typeFace);
  1135.                                 TextSize (itemType.typeSize);
  1136.                                 TextFace (itemType.typeStyle);
  1137.                                 TextLineBox (@freeSpaceStr [1], ORD (freeSpaceStr [0]),
  1138.                                         itemRect, itemType.textJust)
  1139.                             END;
  1140.                     END;
  1141.  
  1142.                 (* Draw the memory indicator frame *)
  1143.                 GetDialogItemRect (processInfoWindow, kMemIndicatorItem,
  1144.                         (*<*)itemRect);
  1145.                 FrameOval (itemRect);
  1146.                 InsetRect ((*◊*)itemRect, 1, 1);
  1147.  
  1148.                 (* Calc angle in the memory indicator that the free memory begins *)
  1149.                 freeAngle := processInfo.processFreeMem * 360 DIV processInfo.
  1150.                         processSize;
  1151.  
  1152.                 (* Draw the memory indicator *)
  1153.                 error := Gestalt (gestaltQuickdrawVersion, (*<*)qdVersion);
  1154.                 IF qdVersion = gestaltOriginalQD THEN
  1155.                     PenPat (qd.black)
  1156.                 ELSE
  1157.                     BEGIN
  1158.                         PmForeColor (kUsedColor);
  1159.                         PmBackColor (0)
  1160.                     END;
  1161.  
  1162.                 (* Draw the used memory part of the memory indicator *)
  1163.                 PaintArc (itemRect, 0, 360 - freeAngle);
  1164.  
  1165.                 (* Set the color of the free memory part of the indicator *)
  1166.                 IF qdVersion = gestaltOriginalQD THEN
  1167.                     PenPat (qd.white)
  1168.                 ELSE
  1169.                     BEGIN
  1170.                         PmForeColor (kFreeColor);
  1171.                         PmBackColor (1)
  1172.                     END;
  1173.  
  1174.                 (* Draw the free memory part of the memory indicator *)
  1175.                 PaintArc (itemRect, 360 - freeAngle, freeAngle);
  1176.  
  1177.                 (* Reset the port characteristics back to normal *)
  1178.                 PenNormal;
  1179.                 IF qdVersion <> gestaltOriginalQD THEN
  1180.                     BEGIN
  1181.                         PmForeColor (1);
  1182.                         PmBackColor (0)
  1183.                     END
  1184.             END
  1185.     END;
  1186.  
  1187.  
  1188. {$S ProcessGuts}
  1189. (*******************************************************************************
  1190. * Public: DrawProcessInfoWindow
  1191. *
  1192. * The Dialog Utility routine, DrawDialogItems, is called to draw all the
  1193. * standard dialog items in the processInfoWindow specified by processInfoWindow.
  1194. * Then, the cosmetic gray lines are drawn.  The memory readouts aren’t drawn
  1195. * because they’re drawn in IdleProcessInfoWindow.
  1196. *******************************************************************************)
  1197.  
  1198.     PROCEDURE DrawProcessInfoWindow (processInfoWindow: WindowPtr);
  1199.  
  1200.         VAR
  1201.             grayLineRect: Rect; {Rectangle of gray line item}
  1202.  
  1203.     BEGIN
  1204.         (* Draw the standard dialog items *)
  1205.         DrawDialogItems (processInfoWindow);
  1206.  
  1207.         (* Draw the two gray, cosmetic, separating lines *)
  1208.         GetDialogItemRect (processInfoWindow, kGrayLineItem0, (*<*)grayLineRect);
  1209.         DrawGrayLine (grayLineRect);
  1210.         GetDialogItemRect (processInfoWindow, kGrayLineItem1, (*<*)grayLineRect);
  1211.         DrawGrayLine (grayLineRect)
  1212.     END;
  1213.  
  1214.  
  1215. {$S ProcessGuts}
  1216. (*******************************************************************************
  1217. * Public: FixProcessInfoMenus
  1218. *
  1219. * If there’s enough memory to work with, the launch items in the File menu are
  1220. * enabled.
  1221. *******************************************************************************)
  1222.  
  1223.     PROCEDURE FixProcessInfoMenus (processInfoWindow: WindowPtr);
  1224.  
  1225.         VAR
  1226.             aMenu: MenuHandle; {Handle to any menu we’re checking on}
  1227.  
  1228.     BEGIN
  1229.         (* Undim the File menu items *)
  1230.         aMenu := GetMenuHandle (mFile);
  1231.         IF NOT FailLowMemory (0) THEN
  1232.             BEGIN
  1233.                 EnableItem (aMenu, iLaunchFore);
  1234.                 EnableItem (aMenu, iLaunchBack);
  1235.                 EnableItem (aMenu, iLaunchTo)
  1236.             END
  1237.     END;
  1238.  
  1239.  
  1240. {$S ProcessGuts}
  1241. (*******************************************************************************
  1242. * Public: CloseProcessInfoWindow
  1243. *
  1244. * This should be pretty easy to figure out.
  1245. *******************************************************************************)
  1246.  
  1247.     PROCEDURE CloseProcessInfoWindow (processInfoWindow: WindowPtr);
  1248.  
  1249.         VAR
  1250.             psnHandle: Handle; {Handle to the PSN of process the window represents}
  1251.  
  1252.     BEGIN
  1253.         DisposeHandle (Handle(GetWRefCon (processInfoWindow)));
  1254.         CloseWindow (processInfoWindow);
  1255.         DisposeDialogItems (processInfoWindow);
  1256.         DisposePtr (Ptr(processInfoWindow))
  1257.     END;
  1258.  
  1259.  
  1260. {$S ProcessGuts}
  1261. (*******************************************************************************
  1262. * Public: IdleAllProcessWindows
  1263. *
  1264. * The process list window and process info windows each have their own idle
  1265. * routine defined in this source file, so the type of window is checked and the
  1266. * appropriate idle routine is called for that window.
  1267. *******************************************************************************)
  1268.  
  1269.     PROCEDURE IdleAllProcessWindows;
  1270.  
  1271.         VAR
  1272.             processWindow: WindowPtr; {Pointer to each process window being idled}
  1273.  
  1274.     BEGIN
  1275.         processWindow := FrontWindow;
  1276.  
  1277.         (* Loop through all windows in the window list *)
  1278.         WHILE processWindow <> NIL DO
  1279.             BEGIN
  1280.                 (* Call the appropriate idle routine if it’s a process window *)
  1281.                 IF IsProcessListWindow (processWindow) THEN
  1282.                     IdleProcessListWindow (processWindow)
  1283.                 ELSE IF IsProcessInfoWindow (processWindow) THEN
  1284.                     IdleProcessInfoWindow (processWindow);
  1285.  
  1286.                 (* Go to the next window in the window list *)
  1287.                 processWindow := WindowPtr(WindowPeek(processWindow)^.nextWindow)
  1288.             END
  1289.     END;
  1290.  
  1291.  
  1292. {$S ProcessGuts}
  1293. (*******************************************************************************
  1294. * Private: AppDAFilter - File filter procedure for apps and files with DAs
  1295. *
  1296. *     This is a Standard File file filter procedure that allows applications and
  1297. * any files with desk accessories in them to show up in the Standard File file
  1298. * list.
  1299. *
  1300. *     Checking to see whether a file is an application is easy enough.  Just
  1301. * check to see whether its type is APPL.  If it is, then it’s an application.
  1302. * Checking on desk accessories is trickier.  Desk accessories can be contained
  1303. * in any type of file.  So if a file isn’t doesn’t have the APPL type, I open
  1304. * the resource fork of the file using HOpenResFile and an access mode of
  1305. * fdRdPerm.  This allows me to open and close the resource file without worrying
  1306. * about that resource file being open by someone else because HOpenResFile with
  1307. * an access mode of fdRdPerm returns a unique access path to this routine.  When
  1308. * the file is open, I check for DRVR resources.  DRVR resources can be either
  1309. * desk accessories or device drivers.  I only want to show files containing desk
  1310. * accessories, so I check on the first character of the DRVR resource’s name.
  1311. * If it’s a null character, then the DRVR is a desk accessory.  If it’s any
  1312. * other character, then it’s a device driver and I ignore it.
  1313. *******************************************************************************)
  1314.  
  1315.     FUNCTION AppDAFilter (fileInfo: CInfoPBPtr): Boolean;
  1316.  
  1317.         CONST
  1318.             kShowIt = FALSE; {FALSE means I do not filter out...}
  1319.  
  1320.         TYPE
  1321.             LongIntPtr = ^LongInt;
  1322.  
  1323.         VAR
  1324.             resRef:     Integer; {File ref num of file being tested}
  1325.             currResRef: Integer; {File ref number of current file}
  1326.             numDrvrs:   Integer; {Number of DRVR resources in file being tested}
  1327.             index:      Integer; {Index into resources of file being tested}
  1328.             drvrRsrc:   Handle;  {Handle to DRVR resource; always NIL master ptr}
  1329.             resID:      Integer; {Resource ID of DRVR resource; ignored}
  1330.             resType:    ResType; {Resource type of DRVR resource; ignored}
  1331.             resName:    Str255;  {Resource name of DRVR resource}
  1332.  
  1333.     BEGIN
  1334.         IF fileInfo^.ioFlFndrInfo.fdType ='APPL' THEN
  1335.             AppDAFilter := kShowIt
  1336.         ELSE
  1337.             BEGIN
  1338.                 (* Assume we don’t show the file *)
  1339.                 AppDAFilter := NOT kShowIt;
  1340.  
  1341.                 (* Want to check rsrcs, not load ’em, including preload resources *)
  1342.                 SetResLoad (FALSE);
  1343.  
  1344.                 (* Save current res file refnum, open the specified rsrc file *)
  1345.                 currResRef := CurResFile;
  1346.                 resRef := HOpenResFile (fileInfo^.ioVRefNum,
  1347.                         LMGetCurDirStore, fileInfo^.ioNamePtr^, fsRdPerm);
  1348.  
  1349.                 (* If couldn’t open resource file, HOpenResFile returns -1 *)
  1350.                 IF (resRef <> -1) THEN
  1351.                     BEGIN
  1352.                         UseResFile (resRef);
  1353.  
  1354.                         (* Count number of DRVR resources in the file *)
  1355.                         numDrvrs := Count1Resources ('DRVR');
  1356.                         IF numDrvrs > 0 THEN
  1357.                             BEGIN
  1358.                                 (* For each DRVR, see if it’s a DA *)
  1359.                                 FOR index := 1 TO numDrvrs DO
  1360.                                     BEGIN
  1361.                                         drvrRsrc := Get1IndResource ('DRVR', index);
  1362.                                         GetResInfo (drvrRsrc, (*<*)resID, (*<*)resType,
  1363.                                                 (*<*)resName);
  1364.         
  1365.                                         (* If first char of name is null, it’s a DA *)
  1366.                                         IF resName [1] = CHR (0) THEN
  1367.                                             AppDAFilter := kShowIt
  1368.                                     END
  1369.                             END;
  1370.                         CloseResFile (resRef)
  1371.                     END;
  1372.         
  1373.                 (* Restore everything back to what it was *)
  1374.                 UseResFile (currResRef);
  1375.                 SetResLoad (TRUE)
  1376.             END
  1377.     END;
  1378.  
  1379.  
  1380. {$S ProcessGuts}
  1381. (*******************************************************************************
  1382. * Private: LaunchCycle - Attempt to launch a process
  1383. *
  1384. * This routine calls the LaunchProcess routine that’s in the UProcessUtils unit.
  1385. * The launchFile parameter specifies the file to launch.  The docList parameter
  1386. * specifies the list of documents to pass to the launched application for it to
  1387. * open or print.  The launchOptions parameter specifies the initial set of
  1388. * launch options to use when launching.  The the section titled “Specifying
  1389. * Launch Options” in the Process Manager chapter of Inside Macintosh VI for the
  1390. * a list and description of the launch options that you can pass in this
  1391. * parameter.
  1392. *
  1393. * If the Process Manager denies the launch, then LaunchProcess returns the
  1394. * resulting error code in the LaunchError flag.  If this happens and if the
  1395. * error happened to be that the machine is in 32-bit addressing mode and the
  1396. * application’s SIZE resource doesn’t have the 32-bit clean flag on, or if there
  1397. * isn’t enough memory to launch the application or desk accessory, then an alert
  1398. * is presented to the user asking if he or she wants to continue anyway.  If the
  1399. * user specifies that he or she does, then launch options are added to the ones
  1400. * passed in the launchOptions parameter which allow 32-bit unclean applications
  1401. * to launch or to allow the launch into available memory, and then LaunchProcess
  1402. * is called again.  This is repeated either until the application or desk
  1403. * accessory is successfully launched, the user chose not to launch it, or until
  1404. * an unrecoverable error occurs.
  1405. *******************************************************************************)
  1406.  
  1407.     PROCEDURE LaunchCycle (launchFile:    FSSpec;
  1408.                            docList:       DocListHnd;
  1409.                            launchOptions: Integer);
  1410.  
  1411.         VAR
  1412.             processNum:    ProcessSerialNumber; {Serial number of launched process}
  1413.             attemptLaunch: Boolean;             {TRUE if continuing launch attempt}
  1414.             result:        Integer;             {Result of caution alert}
  1415.             launchError:   OSErr;               {Launch error code}
  1416.             error:         OSErr;
  1417.  
  1418.     BEGIN
  1419.         (* Repeat until successful launch or cancelled launch *)
  1420.         REPEAT
  1421.             (* Attempt to launch the process *)
  1422.             error := LaunchProcess (launchFile, NIL, docList, launchOptions,
  1423.                     (*<*)processNum, (*<*)launchError);
  1424.  
  1425.             (* Check for launching errors *)
  1426.             IF launchError <> noErr THEN
  1427.                 BEGIN
  1428.                     (* There was a launching error, present to user *)
  1429.                     IF launchError = appModeErr THEN
  1430.                         BEGIN
  1431.                             (* Ask user if it’s OK to launch 32-bit unclean app *)
  1432.                             result := ShowCautionOKCancelAlert (rMiscWrnMessages,
  1433.                                     kMiscWrnUncleanMsg);
  1434.                             IF result = ok THEN
  1435.                                 BEGIN
  1436.                                     (* Try launch again, allowing 32-bit unclean app *)
  1437.                                     launchOptions := BOR (launchOptions,
  1438.                                             launchAllow24Bit);
  1439.                                     attemptLaunch := TRUE
  1440.                                 END
  1441.                             ELSE
  1442.                                 attemptLaunch := FALSE
  1443.                         END
  1444.                     ELSE IF launchError = memFullErr THEN
  1445.                         BEGIN
  1446.                             (* Ask user if it’s OK to launch w/ < requested memory *)
  1447.                             result := ShowCautionOKCancelAlert (rMiscWrnMessages,
  1448.                                     kMiscWrnLaunchMemMsg);
  1449.                             IF result = ok THEN
  1450.                                 BEGIN
  1451.                                     (* Try launch again, with less than requested mem *)
  1452.                                     launchOptions := BOR (launchOptions,
  1453.                                             launchUseMinimum);
  1454.                                     attemptLaunch := TRUE
  1455.                                 END
  1456.                             ELSE
  1457.                                 attemptLaunch := FALSE
  1458.                         END
  1459.                     ELSE
  1460.                         BEGIN
  1461.                             (* Some error we don’t handle happened *)
  1462.                             result := ShowStopAlert (rMiscErrMessages,
  1463.                                     kMiscErrUnknownMsg);
  1464.                             attemptLaunch := FALSE
  1465.                         END
  1466.                 END
  1467.             ELSE IF error <> noErr THEN
  1468.                 BEGIN
  1469.                     result := ShowStopAlert (rMiscErrMessages,
  1470.                             kMiscErrUnknownMsg);
  1471.                     attemptLaunch := FALSE
  1472.                 END
  1473.             ELSE
  1474.                 attemptLaunch := FALSE
  1475.         UNTIL NOT attemptLaunch;
  1476.     END;
  1477.  
  1478.  
  1479. {$S ProcessGuts}
  1480. (*******************************************************************************
  1481. * Public: DoLaunchInFront
  1482. *
  1483. * If the user is launching with documents, then only applications are presented
  1484. * to the user in the standard-file dialog.  If the user only wants to launch
  1485. * without any documents, then both applications and files containing desk
  1486. * accessories are presented to the user.
  1487. *******************************************************************************)
  1488.  
  1489.     PROCEDURE DoLaunchInFront;
  1490.  
  1491.         VAR
  1492.             reply:       StandardFileReply; {Reply from SFGetFile}
  1493.             typeList:    SFTypeList;        {List of file types for SF}
  1494.             launchSpec:  FSSpec;            {Location of selected app/DA}
  1495.             docList:     DocListHnd;        {Handle to the document list}
  1496.             gettingDocs: Boolean;           {True if user still getting docs}
  1497.             launchMode:  LaunchModeCode;    {Current launch mode}
  1498.             error:       OSErr;
  1499.  
  1500.     BEGIN
  1501.         (* Get the user’s choice for a file to launch *)
  1502.         launchMode := GetLaunchMode;
  1503.         IF launchMode = kJustLaunch THEN
  1504.             (* Just launching, so launch applications and DAs *)
  1505.             StandardGetFile (@AppDAFilter, -1, typeList, (*<*)reply)
  1506.         ELSE IF (launchMode = kOpenLaunch) OR (launchMode = kPrintLaunch) THEN
  1507.             BEGIN
  1508.                 (* Launching with documents, so launch applications only *)
  1509.                 typeList [0] := 'APPL';
  1510.                 StandardGetFile (NIL, 1, typeList, (*<*)reply)
  1511.             END;
  1512.  
  1513.         IF reply.sfGood THEN
  1514.             BEGIN
  1515.                 launchSpec := reply.sfFile;
  1516.  
  1517.                 (* Check to see if documents should be opened/printed as well *)
  1518.                 IF (launchMode = kOpenLaunch) OR (launchMode = kPrintLaunch) THEN
  1519.                     BEGIN
  1520.                         (* Create an empty list of documents *)
  1521.                         docList := CreateDocList (launchMode);
  1522.  
  1523.                         (* Keep getting documents until user chooses Cancel *)
  1524.                         gettingDocs := TRUE;
  1525.                         WHILE gettingDocs DO
  1526.                             BEGIN
  1527.                                 StandardGetFile (NIL, -1, typeList, (*<*)reply);
  1528.                                 IF reply.sfGood THEN
  1529.                                     error := AddToDocList (reply.sfFile, (*◊*)docList)
  1530.                                 ELSE
  1531.                                     gettingDocs := FALSE
  1532.                             END
  1533.                     END
  1534.                 ELSE
  1535.                     docList := NIL;
  1536.  
  1537.                 (* Attempt to launch the application *)
  1538.                 LaunchCycle (launchSpec, docList, launchContinue);
  1539.  
  1540.                 (* Dispose of the document list, if there was one *)
  1541.                 IF docList <> NIL THEN
  1542.                     DisposeDocList (docList)
  1543.             END
  1544.     END;
  1545.  
  1546.  
  1547. {$S ProcessGuts}
  1548. (*******************************************************************************
  1549. * Public: DoLaunchInBack
  1550. *
  1551. * For the moment, I’m using SFGetFile to choose files rather than
  1552. * StandardGetFile because StandardGetFile has a bug in that file filtering
  1553. * doesn’t work right.  This is supposed to be fixed in b2, so I’ll change this
  1554. * call once that version is released.  This will make the call to FSMakeFSSpec
  1555. * unnecessary because StandardGetFile returns the FSSpec of the chosen file.
  1556. *******************************************************************************)
  1557.  
  1558.     PROCEDURE DoLaunchInBack;
  1559.  
  1560.         VAR
  1561.             reply:       StandardFileReply; {Reply from SFGetFile}
  1562.             typeList:    SFTypeList;        {List of file types to diplay in SF}
  1563.             launchSpec:  FSSpec;            {Location of selected application}
  1564.             docList:     DocListHnd;        {Handle to the document list}
  1565.             gettingDocs: Boolean;           {True if user still getting docs}
  1566.             launchMode:  LaunchModeCode;    {Current launch mode}
  1567.             error:       OSErr;
  1568.  
  1569.     BEGIN
  1570.         (* Get the user’s choice for an application to launch *)
  1571.         typeList [0] := 'APPL';
  1572.         StandardGetFile (NIL, 1, typeList, (*<*)reply);
  1573.  
  1574.         IF reply.sfGood THEN
  1575.             BEGIN
  1576.                 (* Convert working directory and file name to FSSpec *)
  1577.                 launchSpec := reply.sfFile;
  1578.  
  1579.                 launchMode := GetLaunchMode;
  1580.                 IF (launchMode = kOpenLaunch) OR (launchMode = kPrintLaunch) THEN
  1581.                     BEGIN
  1582.                         (* Create an empty list of documents *)
  1583.                         docList := CreateDocList (launchMode);
  1584.  
  1585.                         (* Keep getting documents until user chooses Cancel *)
  1586.                         gettingDocs := TRUE;
  1587.                         WHILE gettingDocs DO
  1588.                             BEGIN
  1589.                                 StandardGetFile (NIL, -1, typeList, (*<*)reply);
  1590.                                 IF reply.sfGood THEN
  1591.                                     error := AddToDocList (reply.sfFile, (*◊*)docList)
  1592.                                 ELSE
  1593.                                     gettingDocs := FALSE
  1594.                             END
  1595.                     END
  1596.                 ELSE
  1597.                     docList := NIL;
  1598.  
  1599.                 (* Attempt to launch the application *)
  1600.                 LaunchCycle (launchSpec, docList, launchContinue +
  1601.                         launchDontSwitch);
  1602.  
  1603.                 (* Dispose of the document list, if there was one *)
  1604.                 IF docList <> NIL THEN
  1605.                     DisposeDocList (docList)
  1606.             END
  1607.     END;
  1608.  
  1609.  
  1610. {$S ProcessGuts}
  1611. (*******************************************************************************
  1612. * Public: DoLaunchTo
  1613. *
  1614. * For the moment, I’m using SFGetFile to choose files rather than
  1615. * StandardGetFile because StandardGetFile has a bug in that file filtering
  1616. * doesn’t work right.  This is supposed to be fixed in b2, so I’ll change this
  1617. * call once that version is released.  This will make the call to FSMakeFSSpec
  1618. * unnecessary because StandardGetFile returns the FSSpec of the chosen file.
  1619. *******************************************************************************)
  1620.  
  1621.     PROCEDURE DoLaunchTo;
  1622.  
  1623.         VAR
  1624.             reply:       StandardFileReply; {Reply from SFGetFile}
  1625.             typeList:    SFTypeList;        {List of file types to diplay in SF}
  1626.             launchSpec:  FSSpec;            {Location of selected file}
  1627.             docList:     DocListHnd;        {Handle to the document list}
  1628.             gettingDocs: Boolean;           {True if user still getting docs}
  1629.             launchMode:  LaunchModeCode;    {Current launch mode}
  1630.             error:       OSErr;
  1631.  
  1632.     BEGIN
  1633.         (* Get the user’s choice for a file to launch *)
  1634.         launchMode := GetLaunchMode;
  1635.         IF launchMode = kJustLaunch THEN
  1636.             (* Just launching, so launch applications and DAs *)
  1637.             StandardGetFile (@AppDAFilter, -1, typeList, (*<*)reply)
  1638.         ELSE IF (launchMode = kOpenLaunch) OR (launchMode = kPrintLaunch) THEN
  1639.             BEGIN
  1640.                 (* Launching with documents, so launch applications only *)
  1641.                 typeList [0] := 'APPL';
  1642.                 StandardGetFile (NIL, 1, typeList, (*<*)reply)
  1643.             END;
  1644.  
  1645.         IF reply.sfGood THEN
  1646.             BEGIN
  1647.                 (* Convert working directory and file name to FSSpec *)
  1648.                 launchSpec := reply.sfFile;
  1649.  
  1650.                 IF (launchMode = kOpenLaunch) OR (launchMode = kPrintLaunch) THEN
  1651.                     BEGIN
  1652.                         (* Create an empty list of documents *)
  1653.                         docList := CreateDocList (launchMode);
  1654.  
  1655.                         (* Keep getting documents until user chooses Cancel *)
  1656.                         gettingDocs := TRUE;
  1657.                         WHILE gettingDocs DO
  1658.                             BEGIN
  1659.                                 StandardGetFile (NIL, -1, typeList, (*<*)reply);
  1660.                                 IF reply.sfGood THEN
  1661.                                     error := AddToDocList (reply.sfFile, (*◊*)docList)
  1662.                                 ELSE
  1663.                                     gettingDocs := FALSE
  1664.                             END
  1665.                     END
  1666.                 ELSE
  1667.                     docList := NIL;
  1668.  
  1669.                 (* Attempt to launch the application or DA *)
  1670.                 LaunchCycle (launchSpec, docList, launchAllow24Bit);
  1671.  
  1672.                 (* Dispose of the document list, if there was one *)
  1673.                 IF docList <> NIL THEN
  1674.                     DisposeDocList (docList)
  1675.             END
  1676.     END;
  1677.  
  1678.  
  1679. {$S ProcessGuts}
  1680. (*******************************************************************************
  1681. * Public: DoLaunchMode
  1682. *
  1683. * SetLaunchMode does most of the work, and there isn’t much to do.
  1684. *******************************************************************************)
  1685.  
  1686.     PROCEDURE DoLaunchMode (modeItem: Integer);
  1687.  
  1688.     BEGIN
  1689.         CASE modeItem OF
  1690.             iJustLaunch:
  1691.                 SetLaunchMode (kJustLaunch);
  1692.             iOpenLaunch:
  1693.                 SetLaunchMode (kOpenLaunch);
  1694.             iPrintLaunch:
  1695.                 SetLaunchMode (kPrintLaunch)
  1696.         END
  1697.     END;
  1698.  
  1699.  
  1700. {$S ProcessGuts}
  1701. (*******************************************************************************
  1702. * Public: DoBringProcessToFront
  1703. *
  1704. * The List Manager is called to get each selection in the process list window.
  1705. * SetFrontProcess is called with the process serial number of each selected
  1706. * process.  They aren’t immediately brought to the front when SetFrontProcess is
  1707. * called.  Instead, they are scheduled to come to the front in the same order as
  1708. * they were presented to SetFrontProcess.  Once ProcDoggie reenters the main
  1709. * event loop, the Process Manager brings each scheduled process to the front in
  1710. * turn.
  1711. *
  1712. * At the moment, I can’t get ProcDoggie itself to be scheduled.  I assume it’s
  1713. * because SetFrontProcess checks to see if process serial number you passed it
  1714. * is the same as the process serial number of the current process.  If it is, it
  1715. * doesn’t bother to schedule the process.  I’m not quite sure how to work around
  1716. * that.
  1717. *******************************************************************************)
  1718.  
  1719.     PROCEDURE DoBringProcessToFront (processListWindow: WindowPtr);
  1720.  
  1721.         CONST
  1722.             kFindNext = TRUE; {Pass to LGetSelect to find sequence of selections}
  1723.  
  1724.         VAR
  1725.             procList:     ListHandle;         {Handle to List Mgr process list}
  1726.             currCell:     Point;              {Cell that has selection}
  1727.             listInfo:     ProcessListInfoRec; {Process info from List Mgr list}
  1728.             gotSelection: Boolean;            {T if got sel’d cell, F if no more}
  1729.             listInfoLen:  Integer;            {Length of list info in bytes}
  1730.             error:        OSErr;
  1731.  
  1732.     BEGIN
  1733.         (* Get the List Manager’s copy of the process list *)
  1734.         procList := ListHandle(GetWRefCon (processListWindow));
  1735.  
  1736.         (* Keep looping until all selected processes have been brought to front *)
  1737.         currCell.v := 0;
  1738.         currCell.h := 0;
  1739.         gotSelection := TRUE;
  1740.         WHILE gotSelection DO
  1741.             BEGIN
  1742.                 gotSelection := LGetSelect (kFindNext, (*◊*)currCell, procList);
  1743.                 IF gotSelection THEN
  1744.                     BEGIN
  1745.                         listInfoLen := SIZEOF (ProcessListInfoRec);
  1746.                         LGetCell (Ptr(@listInfo), (*◊*)listInfoLen, currCell,
  1747.                                 procList);
  1748.                         error := SetFrontProcess (listInfo.serialNumber);
  1749.                         currCell.v := currCell.v + 1
  1750.                     END
  1751.             END
  1752.     END;
  1753.  
  1754.  
  1755. {$S ProcessGuts}
  1756. (*******************************************************************************
  1757. * Public: DoGetProcessInfo
  1758. *
  1759. * This routine loops until Process Information windows for all selected
  1760. * processes in the Process List window are displayed.  Information for each
  1761. * process in the process list is retrieved from the list itself.  Then, that
  1762. * process is compared against all existing Process Information windows.  If a
  1763. * Process Information window already exists for that process, then that window
  1764. * is simply activated and DoGetProcessInfo exits.  Otherwise, the Process
  1765. * Manager is called to retrieve information for that process.  A new Process
  1766. * Information window is created, and its contents are set to the information
  1767. * retrieved for the process.
  1768. *******************************************************************************)
  1769.  
  1770.     PROCEDURE DoGetProcessInfo (processListWindow: WindowPtr);
  1771.  
  1772.         CONST
  1773.             kFindNext = TRUE; {Pass to LGetSelect to find sequence of selections}
  1774.  
  1775.         VAR
  1776.             procList:          ListHandle;         {Handle to List Mgr proc list}
  1777.             currCell:          Point;              {Cell that has selection}
  1778.             listInfo:          ProcessListInfoRec; {Proc info from List Mgr list}
  1779.             gotSelection:      Boolean;            {T if got sel’d cell, F if none}
  1780.             listInfoLen:       Integer;            {Length of list info in bytes}
  1781.             processInfo:       ProcessInfoRec;     {Info about selected processes}
  1782.             procName:          Str31;              {Name of selected processes}
  1783.             procSpec:          FSSpec;             {File spec of sel’d processes}
  1784.             processInfoWindow: WindowPtr;          {Ptr to new process info window}
  1785.             psnHandle:         Handle;             {Handle to PSN of chosen proc}
  1786.             existingWindow:    WindowPtr;          {Proc info wind if already open}
  1787.             error:             OSErr;
  1788.  
  1789.         PROCEDURE HandleError (messageClass: Integer;
  1790.                                messageIndex: Integer);
  1791.  
  1792.             VAR
  1793.                 result: Integer; {Result of alert; ignored}
  1794.  
  1795.         BEGIN
  1796.             IF processInfoWindow <> NIL THEN
  1797.                 CloseProcessInfoWindow (processInfoWindow);
  1798.             result := ShowStopAlert (messageClass, messageIndex);
  1799.             gError := noErr;
  1800.             EXIT (DoGetProcessInfo)
  1801.         END;
  1802.  
  1803.     BEGIN
  1804.         (* Get the List Manager’s copy of the process list *)
  1805.         procList := ListHandle(GetWRefCon (processListWindow));
  1806.  
  1807.         (* Keep looping until all selected processes have been brought to front *)
  1808.         currCell.v := 0;
  1809.         currCell.h := 0;
  1810.         gotSelection := TRUE;
  1811.         WHILE gotSelection DO
  1812.             BEGIN
  1813.                 gotSelection := LGetSelect (kFindNext, (*◊*)currCell, procList);
  1814.                 IF gotSelection THEN
  1815.                     BEGIN
  1816.                         listInfoLen := SIZEOF (ProcessListInfoRec);
  1817.                         LGetCell (Ptr(@listInfo), (*◊*)listInfoLen, currCell,
  1818.                                 procList);
  1819.  
  1820.                         (* See if proc info wind already exists for selected proc *)
  1821.                         existingWindow := FindProcessInfoWindow (listInfo.
  1822.                                 serialNumber);
  1823.                         IF existingWindow <> NIL THEN
  1824.                             SelectWindow (existingWindow)
  1825.                         ELSE
  1826.                             BEGIN
  1827.                                 (* Get information about an open process *)
  1828.                                 processInfo.processInfoLength :=
  1829.                                         SIZEOF (ProcessInfoRec);
  1830.                                 processInfo.processName := @procName;
  1831.                                 processInfo.processAppSpec := @procSpec;
  1832.                                 error := GetProcessInformation (listInfo.serialNumber,
  1833.                                         (*◊*)processInfo);
  1834.                                 IF error <> noErr THEN
  1835.                                     HandleError (rMiscErrMessages, kMiscErrUnknownMsg);
  1836.  
  1837.                                 (* Create the process information window *)
  1838.                                 processInfoWindow := CreateProcessInfoWindow;
  1839.                                 IF processInfoWindow <> NIL THEN
  1840.                                     BEGIN
  1841.                                         (* Put handle to PSN into refCon *)
  1842.                                         psnHandle := NewHandleMargin (SIZEOF
  1843.                                                 (ProcessSerialNumber), kAllocApp,
  1844.                                                 NOT kAllocClr);
  1845.                                         IF psnHandle = NIL THEN
  1846.                                             HandleError (rMemErrMessages,
  1847.                                                     kMemErrProcInfoOpenMsg);
  1848.                                         BlockMove (Ptr(@processInfo.processNumber),
  1849.                                                 psnHandle^, SIZEOF (ProcessSerialNumber));
  1850.                                         SetWRefCon (processInfoWindow,
  1851.                                                 LongInt(psnHandle));
  1852.  
  1853.                                         (* Update dlog items to reflect proc info *)
  1854.                                         SetUpProcessInfoItems (processInfoWindow,
  1855.                                                 processInfo);
  1856.                                     END
  1857.                                 ELSE
  1858.                                     gotSelection := FALSE
  1859.                             END;
  1860.  
  1861.                         (* Go to the next cell *)
  1862.                         currCell.v := currCell.v + 1
  1863.                     END
  1864.             END
  1865.     END;
  1866.  
  1867.  
  1868. {$S ProcessGuts}
  1869. (*******************************************************************************
  1870. * Public: DoTerminateProcess
  1871. *
  1872. * The List Manager is used to get all of the selected processes in
  1873. * processListWindow.  The process serial number of each of these processes is
  1874. * extracted and is then used when calling TerminateProcess.
  1875. *******************************************************************************)
  1876.  
  1877.     PROCEDURE DoTerminateProcess (processListWindow: WindowPtr);
  1878.  
  1879.         CONST
  1880.             kFindNext = TRUE; {Pass to LGetSelect to find sequence of selections}
  1881.  
  1882.         VAR
  1883.             procList:     ListHandle;         {Handle to List Mgr process list}
  1884.             currCell:     Point;              {Cell that has selection}
  1885.             listInfo:     ProcessListInfoRec; {Process info from List Mgr list}
  1886.             listInfoLen:  Integer;            {Length of list info in bytes}
  1887.             gotSelection: Boolean;            {T if got sel’d cell, F if none}
  1888.             error:        OSErr;
  1889.  
  1890.         PROCEDURE HandleError (messageClass: Integer;
  1891.                                messageIndex: Integer);
  1892.  
  1893.             VAR
  1894.                 result: Integer; {Result of alert; ignored}
  1895.  
  1896.         BEGIN
  1897.             result := ShowStopAlert (messageClass, messageIndex);
  1898.             gError := noErr;
  1899.             EXIT (DoTerminateProcess)
  1900.         END;
  1901.  
  1902.     BEGIN
  1903.         (* Get the List Manager’s copy of the process list *)
  1904.         procList := ListHandle(GetWRefCon (processListWindow));
  1905.  
  1906.         (* Keep looping until all selected processes have been terminated *)
  1907.         currCell.v := 0;
  1908.         currCell.h := 0;
  1909.         gotSelection := TRUE;
  1910.         WHILE gotSelection DO
  1911.             BEGIN
  1912.                 gotSelection := LGetSelect (kFindNext, (*◊*)currCell, procList);
  1913.                 IF gotSelection THEN
  1914.                     BEGIN
  1915.                         listInfoLen := SIZEOF (ProcessListInfoRec);
  1916.                         LGetCell (Ptr(@listInfo), (*◊*)listInfoLen, currCell,
  1917.                                 procList);
  1918.  
  1919.                         (* Kill the specified process *)
  1920.                         error := TerminateProcess (listInfo.serialNumber);
  1921.                         IF error <> noErr THEN
  1922.                             HandleError (rMiscErrMessages, kMiscErrUnknownMsg);
  1923.  
  1924.                         (* Go to the next cell *)
  1925.                         currCell.v := currCell.v + 1
  1926.                     END
  1927.             END
  1928.     END;
  1929.